package timingwheel

import (
	"sync"
	"time"
)

type TimingWheel struct {
	prev      *TimingWheel
	next      *TimingWheel
	wheelSize int // 格子数量
	tick      int
	lock      sync.Mutex
	wheels    map[int]*Grid
	tickMs    int // 每一格的间隔，单位
	interval  int // 每一轮的时间间隔
	level     int
}

func NewTimingWheel(wheelSize int, tickMs int) *TimingWheel {
	t := &TimingWheel{
		wheelSize: wheelSize,
		lock:      sync.Mutex{},
		interval:  wheelSize * tickMs,
		tickMs:    tickMs,
		tick:      0,
		wheels:    make(map[int]*Grid),
	}
	return t
}

// 指针往前走
func (w *TimingWheel) forward() {
	if (w.tick + 1) > w.wheelSize {
		// 走完一圈了
		w.tick = 0
		if !w.isTail() {
			// 通知后一层往前走
			w.next.forward()
			return
		}
	}
	if w.wheels[w.tick] == nil {
		w.tick++
		return
	}
	if w.level > 0 {
		//log.Printf("当前位置 :%d %d", w.level, w.Pos)
		// 如果不是第一级,取出任务,并且向上一级投递
		if w.wheels[w.tick].TaskCount() > 0 {
			w.wheels[w.tick].IterateTask(func(task *Task) {
				delay := task.Delay.Milliseconds() % int64(w.tickMs)
				task.Delay = time.Duration(delay) * time.Millisecond
				w.prev.pushTask(task)
			})
			w.wheels[w.tick].ClearTask()
		}
		w.tick++
		return
	}
	go w.wheels[w.tick].Run()
	w.tick++
}

func (w *TimingWheel) pushTask(task *Task) {
	taskDelay := task.Delay.Milliseconds()
	if taskDelay <= 0 {
		task.Run()
		return
	}
	if taskDelay >= int64(w.interval) {
		// 超过限制，创建新的,并且往后投递
		if w.isTail() {
			w.NewTimingWheel()
		}
		w.next.pushTask(task)
		return
	}
	if taskDelay < int64(w.tickMs) && !w.isHead() {
		// 如果低于格子间隔时间，再往上一级投
		w.prev.pushTask(task)
		return
	}
	tickPos := 0
	tickPos = int(taskDelay) / (w.tickMs) // 取整数
	wheelIndex := tickPos + w.tick - 1
	if wheelIndex < 0 {
		wheelIndex = 0
	}
	if wheelIndex >= w.wheelSize {
		wheelIndex = wheelIndex - w.wheelSize
	}
	//log.Printf("投递格子 level：%d tickPos：%d tick: %d wheelIndex: %d wheelSize:%d", w.level, tickPos, w.tick, wheelIndex, w.wheelSize)
	if _, ok := w.wheels[wheelIndex]; !ok {
		w.wheels[wheelIndex] = NewGrid()
	}
	w.wheels[wheelIndex].PushTask(task)
}

func (w *TimingWheel) NewTimingWheel() *TimingWheel {
	timingWheel := NewTimingWheel(w.wheelSize, w.interval)
	timingWheel.level = w.level + 1
	timingWheel.prev = w
	w.next = timingWheel
	//log.Println("新建：", timingWheel.level)
	return timingWheel
}
func (w *TimingWheel) isHead() bool {
	return w.prev == nil
}
func (w *TimingWheel) isTail() bool {
	return w.next == nil
}
